﻿using Castle.DynamicProxy;
using System;
using System.Collections;
using System.Linq;
using System.Reflection;

namespace Light.Aop
{
    /// <summary>
    /// 基于Castle.Core、Castle.Windsor的缓存拦截器
    /// </summary>
    public class CacheInterceptor : ICacheInterceptor
    {
        public void Intercept(IInvocation invocation)
        {
            try
            {
                #region

                MethodInfo method = null;
                if (invocation.Arguments.Length == 0)
                {
                    method = invocation.InvocationTarget.GetType().GetMethod(invocation.Method.Name);
                }
                else
                {
                    Type[] types = invocation.Arguments.Select(p => p.GetType()).ToArray();
                    method = invocation.InvocationTarget.GetType().GetMethod(invocation.Method.Name, types);
                }
                CacheMethodAttribute attr = method.GetCustomAttribute<CacheMethodAttribute>();
                if (attr != null)
                {
                    try
                    {
                        using (ServiceStack.Redis.IRedisClient redis = Light.Cache.RedisHelper.GetClient())
                        {
                            bool isUpdate = attr.UpdateCache;
                            string keyPrefix = Helper.GetKeyPrefix(method, attr);

                            if (isUpdate && !String.IsNullOrEmpty(keyPrefix)) //删除缓存，下次查询时，重新获取最新数据
                            {
                                var keys = redis.GetAllKeys();
                                foreach (var key in keys)
                                {
                                    if (key.StartsWith(keyPrefix))
                                    {
                                        redis.Remove(key);
                                    }
                                }
                                invocation.Proceed();
                            }
                            else //尝试读取缓存，读不到，则从数据库获取并添加缓存
                            {
                                double expiresSeconds = attr.ExpiresSeconds;
                                string key = keyPrefix + "_" + Helper.GetCacheKey(invocation);

                                var ser = new ServiceStack.Redis.Support.ObjectSerializer();
                                var value = redis.Get<byte[]>(key);
                                if (value == null || value.Length == 0)
                                {
                                    invocation.Proceed();
                                    redis.Set(key, ser.Serialize(invocation.ReturnValue), TimeSpan.FromSeconds(expiresSeconds));
                                }
                                else
                                {
                                    invocation.ReturnValue = ser.Deserialize(value);
                                }
                            }
                        }
                    }
                    catch
                    {
                        invocation.Proceed();
                    }
                }
                else
                {
                    invocation.Proceed();
                }

                #endregion
            }
            catch (Exception ex)
            {
                Light.Utility.TextHelper.Console("aop.log", ex.Message, true);
                invocation.Proceed();
            }
        }
    }
}
